//
//  PlayHistory.swift
//  GhostWorld
//
//  Created by Kam on 2018/6/5.
//  Copyright © 2018 LKGood. All rights reserved.
//

import Foundation
import CoreData

private let entityName = CoreDataEntityName.playHistory.rawValue
private let context = CoreDataHelper.getContext()

struct PlayHistoryItem {
  var audio: Audio
  var album: Album
  var playDate: Date
  var relatedAudios: [Audio]?
}

extension PlayHistory {
  
  static private func get(albumID: Int, audioID: Int) -> PlayHistory? {
    let request = NSFetchRequest<PlayHistory>()
    let entity = NSEntityDescription.entity(forEntityName: entityName, in: context)
    request.entity = entity
    var predicateString = "audioID = \(audioID) AND albumID = \(albumID)"
    if let currentUser = UserManager.shared.currentUser {
      predicateString = "\(predicateString) AND userID = \(currentUser.identifier)"
    } else {
      predicateString = "\(predicateString) AND userID = \(-1)"
    }
    request.predicate = NSPredicate(format: predicateString)
    do {
      if let currentHistory = try context.fetch(request).first {
        return currentHistory
      } else {
        return nil
      }
    } catch let error {
      loggingPrint(error)
      return nil
    }
  }
  
  static func add(albumID: Int, audioID: Int, relatedAudios: [Audio]?) throws {
    func process(playHistory: PlayHistory, audios: [Audio]?) {
      if audios == nil {
        playHistory.relatedAudioIDs = ""
      } else {
        playHistory.relatedAudioIDs = audios!.map{ "\($0.id)" }.joined(separator: ",")
      }
    }
    
    if let currentHistory = get(albumID: albumID, audioID: audioID) {
      // update play date of current history
      currentHistory.playDate = Date()
      process(playHistory: currentHistory, audios: relatedAudios)
    } else {
      // keep history count to 20
      let request = NSFetchRequest<PlayHistory>()
      let entity = NSEntityDescription.entity(forEntityName: entityName, in: context)
      request.entity = entity
      if let currentUser = UserManager.shared.currentUser {
        request.predicate = NSPredicate(format: "userID = \(currentUser.identifier)")
      } else {
        request.predicate = NSPredicate(format: "userID = \(-1)")
      }
      request.sortDescriptors = [NSSortDescriptor(key: "playDate", ascending: false)]
      
      do {
        var historys = try context.fetch(request)
        
        while historys.count >= 20 {
          let removeHistory = historys.removeLast()
          context.delete(removeHistory)
        }
        
        // add new history
        let history = NSEntityDescription.insertNewObject(forEntityName: entityName, into: context) as! PlayHistory
        history.albumID = Int16(albumID)
        history.audioID = Int16(audioID)
        history.playDate = Date()
        process(playHistory: history, audios: relatedAudios)
        
        if let currentUser = UserManager.shared.currentUser {
          history.userID = Int16(currentUser.identifier)
        } else {
          history.userID = -1
        }
      } catch let error {
        loggingPrint(error)
        throw error
      }
    }
    
    do {
      try context.save()
    } catch let error {
      loggingPrint(error)
      throw error
    }
  }
  
  static func getAll() -> [PlayHistoryItem]? {
    let request = NSFetchRequest<PlayHistory>()
    let entity = NSEntityDescription.entity(forEntityName: entityName, in: context)
    request.entity = entity
    if let currentUser = UserManager.shared.currentUser {
      request.predicate = NSPredicate(format: "userID = \(currentUser.identifier)")
    } else {
      request.predicate = NSPredicate(format: "userID = \(-1)")
    }
    request.sortDescriptors = [NSSortDescriptor(key: "playDate", ascending: false)]
    
    do {
      let historys = try context.fetch(request)
      var items = [PlayHistoryItem]()
      historys.forEach {
        if let album = EntityManager.fetch(Album.self, fetchType: .id(Int($0.albumID)))?.first,
          let audio = EntityManager.fetch(Audio.self, fetchType: .id(Int($0.audioID)))?.first,
          let date = $0.playDate {
          
          var relatedAudios: [Audio]? = nil
          if $0.relatedAudioIDs != nil {
            let audioIDsArray = $0.relatedAudioIDs!.split(separator: ",")
            if audioIDsArray.count > 0 {
              relatedAudios = [Audio]()
              for audioID in audioIDsArray {
                if let audio = EntityManager.fetch(Audio.self, fetchType: .id(Int(audioID)!))?.first {
                  relatedAudios?.append(audio)
                }
              }
              if relatedAudios?.count == 0 {
                relatedAudios = nil
              }
            }
          }
          let playHistoryItem = PlayHistoryItem (
            audio: audio,
            album: album,
            playDate: date,
            relatedAudios: relatedAudios
          )
          items.append(playHistoryItem)
        }
      }
      return items
    } catch let error {
      loggingPrint(error)
      return nil
    }
  }
  
  static func remove(albumID: Int, audioID: Int) throws {
    if let currentHistory = get(albumID: albumID, audioID: audioID) {
      context.delete(currentHistory)
      do {
        try context.save()
      } catch let error {
        loggingPrint(error)
        throw error
      }
    }
  }
}
